import mimeJSON from '@assets/json/mime.json'
import { ElFormInst } from '@declare/element-plus'
import { Ref } from 'vue'

/**
 * 生成随机字符串
 * @param {Number} len 字符长度
 */
export function random(len = 4, prefix?: string) {
  const code = Math.random()
    .toString(16)
    .slice(2, len + 2)
  return prefix ? `${prefix}${code}` : code
}

/**
 * 通过 文件对象/文件名 获取相关详情
 * @param {File|String} file 文件对象或文件名
 * @returns {Object}
 */
export function fileDetail(file: File | string) {
  if (file instanceof File) {
    file = file.name
  }
  const match = file.match(/\.\w+$/)
  const suffix = match && match[0] ? match[0] : ''
  const detail = mimeJSON.filter((item) => item.suffix === suffix)[0] || {
    suffix,
    mime: '',
    describe: '当前数据池中未包含该扩展名的相关信息',
  }
  return {
    name: file.replace(new RegExp(`${suffix}$`), ''), // 文件名
    suffix, // 文件扩展名
    mime: detail.mime, // MIME 类型
    describe: detail.describe, // 描述
  }
}

/**
 * 获取文件后缀（扩展名）
 * @param {File|String} file 文件对象或文件名
 * @returns {String}
 */
export function fileSuffix(file: File | string) {
  if (file instanceof File) {
    file = file.name
  }
  const match = `${file}`.match(/\.\w+$/)
  if (match && match[0]) {
    return match[0]
  }
  return ''
}

/**
 * @param {File|Blob} file 二进制对象（File类型 或 Blob类型）
 * @param {String} type 以怎样的方式读取
 * @param {String} encoding 传入一个字符串类型的编码类型，如缺省，则默认为“utf-8”类型
 * @return {Promise<String | ArrayBuffer>}
 */
export function fileRead<T extends keyof FileReadMap>(file: File | Blob, type: T | null, encoding = 'utf-8'): Promise<FileReadMap[T]> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    // 返回错误信息
    reader.addEventListener('error', function () {
      reject(new Error('FileReader 读取资源失败'))
    })
    // 读取成功，返回读取到的资源
    reader.addEventListener('load', function () {
      resolve(reader.result as FileReadMap[T])
    })
    // 不同的读取方式
    switch (type) {
      case 'text':
        reader.readAsText(file, encoding)
        break
      case 'base64':
        reader.readAsDataURL(file)
        break
      // case 'buffer':
      default:
        reader.readAsArrayBuffer(file)
        break
    }
  })
}

/**
 * 加载远程资源为 File
 * @param {String} url 服务器资源地址
 * @param {String} name 文件名
 * @return {Promise<File>}
 */
export function fileDownload(url: string, name?: string) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.responseType = 'blob'
    xhr.addEventListener('load', function () {
      const file = new File([xhr.response], name ?? url.split('/').pop()!, {
        type: xhr.response.type,
        lastModified: Date.now(),
      })
      resolve(file)
    })
    xhr.addEventListener('error', reject)
    xhr.send()
  })
}

/**
 * 表单验证的逻辑部分
 * @param {Ref<boolean>} haveInHand 表单锁，避免重复提交
 * @param {Ref<ElFormInst>} ruleForm 表单的 ref 实例
 * @param {Function} success 表单验证通过后执行的回调函数，返回一个 Promise<void>
 * @returns {void}
 */
export function ruleFormValidate(haveInHand: Ref<boolean>, ruleForm: Ref<ElFormInst>, success: () => Promise<void>) {
  if (haveInHand.value) return
  haveInHand.value = true
  ruleForm.value.validate((valid) => {
    if (valid) {
      success().finally(() => (haveInHand.value = true))
    } else {
      haveInHand.value = false
    }
  })
}

/**
 * 封装 Promise.allSettled，待所有异步完成后，如果存在错误，返回第一个错误，否则返回空 Promise
 */
export function allSettled<T>(iterable: T[]) {
  return Promise.allSettled(iterable).then((results) => {
    for (let i = 0; i < results.length; i++) {
      if (results[i].status === 'rejected') {
        throw new Error((results[i] as PromiseRejectedResult).reason)
      }
    }
  })
}
